package com.tim.helper;

import com.alibaba.fastjson.JSON;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.server.ZooKeeperServerMain;
import org.junit.Test;
import org.springframework.util.ReflectionUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 输出dubbo直调命令，末尾无分号
 *  https://cn.dubbo.apache.org/zh-cn/docsv2.7/user/references/telnet/
 */
public class DubboHelper {

    @Test
    public void printDubboCMD() throws Exception {
        String info = "com.xqxc.api.impl.plat.marketing.activity.ActivityGoodsServiceImpl.getDefaultActivityInfo" +
                "";
        int addrType = 0;// 0本地，1dev，2test

        String classFullName, methodName, argClassFullName, argJson="", hostPort;
        StringBuilder cmdBuilder = new StringBuilder();

        // 解析类全名、方法名
        info = info.trim();
        int index = info.indexOf("#");
        if(index>-1){
            classFullName = info.substring(0, index);
            methodName = info.substring(index+1);
        }else{
            index = info.lastIndexOf(".");
            classFullName = info.substring(0, index);
            methodName = info.substring(index+1);
        }

        // 反射获取类、方法；构建argJson参数
        try{
            Class<?> clazz = Class.forName(classFullName);
            Method[] methods = clazz.getMethods();
            for(Method method : methods){
                if(method.getName().equals(methodName)){
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    if(parameterTypes.length>0){
                        Class<?> parameterType = parameterTypes[0];
                        argClassFullName = parameterType.getName();
                        Object param = parameterType.newInstance();
                        argJson = JSON.toJSONString(param)
                                .replaceFirst("\\{", "{ \"class\":\""+argClassFullName+"\", ");
                    }
                }
            }
        }catch (ClassNotFoundException e){
            argJson = "{\"class\":\"\", }";
        }

        hostPort = addrType==0 ? "" : "";
        cmdBuilder.append("telnet "+hostPort)
                .append("\n")
                .append(String.format("invoke %s(%s)", info, argJson))
                .append("\n");
        System.out.println(cmdBuilder);
    }

    /**
     * 遍历文件夹plat开头的子级目录；输出服务-端口关系
     *  -api结尾的孙级目录；进入src/main/java目录，再进入com.xqxc.api目录，查找服务前缀
     *  -impl结尾的孙级目录；进入src/main/resources/config目录，读取dubbo.config dubbo.provider.port属性
     */
    private String getLocalServiceHostPort(String classFullName) throws IOException {
        String path = "E:\\gitlab\\xqxc\\plat";

        Map<String, String> result = new HashMap<>();
        File dirFile = new File(path);
        File[] platChildDirs = dirFile.listFiles(filePath -> filePath.isDirectory() && filePath.getName().startsWith("plat"));
        for(File platChildDir : platChildDirs){
            String servicePkgPre="", hostPort="";
            for(File platSunDir : platChildDir.listFiles(File::isDirectory)){
                String platSunDirName = platSunDir.getName();
                if(platSunDirName.endsWith("-api")){
                    servicePkgPre = "com.xqxc.api";
                    Path serviceSeachPath = Paths.get(platSunDir.getAbsolutePath(), "src/main/java/com/xqxc/api");
//                    System.out.println(serviceSeachPath);
                    File searchDir = serviceSeachPath.toFile();
                    File[] files = searchDir.listFiles();
                    while(files!=null && files.length==1){
                        searchDir = files[0];
                        servicePkgPre = servicePkgPre+"."+searchDir.getName();
                        files = searchDir.listFiles();
                    }
                }else if(platSunDirName.endsWith("-impl")){
                    Path dubboConfigFilePath = Paths.get(platSunDir.getAbsolutePath(), "src/main/resources/config/dubbo.conf");
                    hostPort = "localhost "+Files.readAllLines(dubboConfigFilePath).stream()
                            .filter(e->e.startsWith("dubbo.provider.port"))
                            .map(e->e.split("=")[1])
                            .findFirst().get();
                }
            }
//            System.out.println(servicePkgPre+"："+hostPort);
            if(servicePkgPre!=null && servicePkgPre.trim().length() > 0){
                result.put(servicePkgPre, hostPort);
            }
        }
//        result.forEach((k, v) -> System.out.println(k+"："+v));

        // 读取项目dubbo端口配置。根据类全名，找到最长的前缀匹配，获取端口
        String hostPort =" HOST PORT";
        String bestMatchPre = "";// 最长的前缀匹配。port选择最长匹配对应的端口
        for(String pre : result.keySet()){
            if(classFullName.startsWith(pre)){
                if(bestMatchPre.length()<pre.length()){
                    bestMatchPre = pre;
                    hostPort = result.get(pre);
                }
            }
        }
        return hostPort;
    }

    /**
     * 按demo连接zk；获取/dubbo/SERVICE/providers子节点，解析hostPort
     *  test环境
     *  47.96.234.201:2181  每日说
     *  172.28.162.234:2181  贝小集
     *
     */
    String zkHostPort = "";
    private String getZkServiceHostPort(String classFullName, int addrType) throws Exception {
        String hostPort =" HOST PORT";
        ZooKeeper zooKeeper=null;
        try{
            if(zkHostPort==null || zkHostPort.length()==0){
                // 默认读取dev配置
                Properties properties = new Properties();
        String filePath =
            addrType == 1
                ? "E:\\gitlab\\xqxc\\settings\\dev\\xqxc-dev.properties"
                : "E:\\gitlab\\xqxc\\settings\\test\\xqxc-test.properties";
                properties.load( new FileInputStream(filePath));
                zkHostPort = properties.getProperty("dubbo.register.addr")
                        .substring("zookeeper://".length());

            }
            zooKeeper = new ZooKeeper( zkHostPort, 1000 * 10, System.out::println);
            // 获取providers子节点
            List<String> children = zooKeeper.getChildren(String.format("/dubbo/%s/providers", classFullName), false);
            if(children==null || children.size()==0){
                String provider = children.get(0);
                provider = URLDecoder.decode(provider);
//                System.out.println(provider);
                int length = "dubbo://".length();
                hostPort = provider.substring(length, provider.indexOf("/", length))
                        .replace(":", " ");
            }
        }finally{
            if(zooKeeper!=null){
                zooKeeper.close();
            }
        }
        return hostPort;
    }

    @Test// find . |grep Service；java文件夹列出接口路径，替换
    public void findServices(){
        String infos = "";
        {
            infos = "";
        }
        {
            infos = "";
        }
        infos = infos.replaceAll("\\./java/", "")
                .replaceAll("/", ".")
                .replace(".java", "");
        System.out.println(infos);
    }

    @Test// dubboAdmin application搜索，复制
    public void dubboAdminServices(){
        String infos = "";
        {
            infos = "";
        }
        {
            infos = "";
        }
        List<String> provided = new ArrayList<>();
        List<String> consumed = new ArrayList<>();
        for(String info : infos.split("[\r\n]+")){
            String[] split = info.trim().split("\\s+");
            if("provided".equals(split[1])){
                provided.add(split[0]);
            }else if("consumed".equals(split[1])){
                consumed.add(split[0]);
            }else{
                System.err.println(info);
            }
        }
        System.out.println("------provided ");
        provided.forEach(System.out::println);
        System.out.println("------consumed ");
        consumed.forEach(System.out::println);
    }

    /*api配置： https://cn.dubbo.apache.org/zh-cn/docs/references/configuration/api/*/
    @Test
    public void invokeReference() throws Exception {
        Class<DubboHelper> clz = DubboHelper.class;
        String methodName="";
        Object[] args = new Object[0];
        // 当前应用配置
        ApplicationConfig application = new ApplicationConfig();
        application.setName("ltl-test");
        application.setQosEnable(false);

        // 连接注册中心配置
        RegistryConfig registry = new RegistryConfig();
        registry.setAddress("zookeeper://10.20.130.230:2181");

        // 注意：ReferenceConfig为重对象，内部封装了与注册中心的连接，以及与服务提供方的连接
        // 引用远程服务
        ReferenceConfig<DubboHelper> reference = new ReferenceConfig<>(); // 此实例很重，封装了与注册中心的连接以及与提供者的连接，请自行缓存，否则可能造成内存和连接泄漏
        reference.setApplication(application);
        reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
        reference.setInterface(clz);
        reference.setVersion("1.0.0");

        // 和本地bean一样使用demoService
        // 注意：此代理对象内部封装了所有通讯细节，对象较重，请缓存复用
        DubboHelper demoService = reference.get();
        Method method = clz.getMethod(methodName, clz);
        ReflectionUtils.invokeMethod(method, demoService, args);
    }
    // 注释掉了不同环境的地址
    String addr = "localhost:2181";   // local
//    String addr = "10.1.56.164:2181";  // dev
//    String addr = "10.1.52.164:2181";  // sit
//    String addr = "10.1.48.164:2181";  // uat

    @Test
    public void readZkProviderDetail() throws Exception {
        // 注释掉了不同环境的地址
        String addr = "localhost:2181";   // local
//    String addr = "10.1.56.164:2181";  // dev
//    String addr = "10.1.52.164:2181";  // sit
//    String addr = "10.1.48.164:2181";  // uat

        String fullname = "com.fbank.core.fund.api.service.ifms.FinishStateService";
        String child = "/providers";

        ZooKeeper zooKeeper = new ZooKeeper(addr, 1000 * 10, System.out::println);
        List<String> children = zooKeeper.getChildren("/dubbo/"+fullname+child, false);
        System.out.println("SSSSSSSSSSSSSSSSSSSSSSSSS");
        children = children.stream().map(URLDecoder::decode).collect(Collectors.toList());
        System.out.println(children);
    }

    @Test
    public void readZkConsumers() throws Exception {

        String infos = "";
        {
            infos = "";
        }
        {
            infos = "";
        }
        Map<String, List<String>> map = new LinkedHashMap<>();
        String child = "/consumers";
        ZooKeeper zooKeeper = new ZooKeeper( addr, 1000 * 10, System.out::println);
        for(String info : infos.trim().split("\n")){
            info = info.split("\\[")[0];
            String[] strs = parseInfo(info);
            String group = strs[0];
            String fullname = strs[1];
            String version = strs[2];
            List<String> children = zooKeeper.getChildren("/dubbo/"+fullname+child, false);
            children = children.stream().map(URLDecoder::decode)
                    .filter(e->"".equals(group)||e.contains("group="+group))
                    .filter(e->"".equals(version)||e.contains("version="+version))
                    .map(e->{
                        return Arrays.stream(e.substring(e.indexOf('?')+1).split("&"))
                                .filter(item->item.startsWith("application"))
                                .findFirst().orElse("NONE")
                                .replace("application=",  "");
                    }).distinct()
                    .collect(Collectors.toList());

//            System.out.println(children);
            map.put(info, children);
        }

        System.out.println("SSSSSSSSSSSSSSSSSSSSSSSSS");
        map.forEach((k,v)->System.out.println(k+""+v));

    }

    @Test
    public void readZkProviders() throws Exception {

        String infos = "";
        {
            infos = "";
        }
        {
            infos = "";
        }
        Map<String, List<String>> map = new LinkedHashMap<>();

        String child = "/providers";
        ZooKeeper zooKeeper = new ZooKeeper( addr, 1000 * 10, System.out::println);
        for(String info : infos.trim().split("\n")){
            info = info.split("\\[")[0];
            String[] strs = parseInfo(info);
            String group = strs[0];
            String fullname = strs[1];
            String version = strs[2];
            List<String> children = zooKeeper.getChildren("/dubbo/"+fullname+child, false);
            children = children.stream().map(URLDecoder::decode)
                    .filter(e->"".equals(group)||e.contains("group="+group))
                    .filter(e->"".equals(version)||e.contains("version="+version))
                    .map(e->{
                        return Arrays.stream(e.substring(e.indexOf('?')+1).split("&"))
                                .filter(item->item.startsWith("application"))
                                .findFirst().orElse("NONE")
                                .replace("application=",  "");
                    })
                    .distinct()
                    .collect(Collectors.toList());

//            System.out.println(children);
            map.put(info, children);
        }

        System.out.println("SSSSSSSSSSSSSSSSSSSSSSSSS");
        map.forEach((k,v)->System.out.println(k+"="+v));
    }

    private String[] parseInfo(String info){
        String fullname = info.trim();
        String group,version;
        int index1 = fullname.indexOf("/");
        if(index1>0){
            group=fullname.substring(0, index1);
            fullname = fullname.substring(index1+1);
        }else{
            group="";
        }
        int index2 = fullname.indexOf(":");
        if(index2>0){
            version = fullname.substring(index2+1);
            fullname = fullname.substring(0, index2);
        }else{
            version="";
        }
        return new String[]{group, fullname, version};
    }

}
